package com.ucode.hrm.handle;

import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;

/**
 * 计算两个 LocalDateTime 之间的请假时长（按正常上班时间计算）
 * 算法特点
 * 1、分段计算:将请假周期拆分成单日处理，自动跳过非工作日
 * 2、时间累加：精确计算每个工作日的有效工时
 * 3、午休处理：自动扣除午休时间段的工作时长
 * 4、半天取整：符合企业常见请假计算规则
 */
public class LeaveCalculator {
    // 定义工作时间段（可修改）
    private static final LocalTime WORK_START = LocalTime.of(9, 0);    // 上班时间
    private static final LocalTime WORK_END = LocalTime.of(18, 0);     // 下班时间
    private static final LocalTime LUNCH_START = LocalTime.of(12, 0);   // 午休开始
    private static final LocalTime LUNCH_END = LocalTime.of(13, 0);     // 午休结束
    private static final double HALF_DAY_HOURS = 4.0;                  // 半天对应工时（小时）
    
    /**
     * 计算两个时间点之间的请假时长（按0.5天为最小单位）
     * @param start 请假开始时间
     * @param end 请假结束时间
     * @return 请假天数（0.5的倍数，如1.0、1.5）
     */
    public static double calculateLeave(LocalDateTime start, LocalDateTime end) {
        long totalMinutes = 0;
        LocalDateTime current = start;
        
        while (current.isBefore(end)) {
            // 跳过非工作日（周末）
            if (isWeekend(current)) {
                current = current.plusDays(1).with(WORK_START);
                continue;
            }
            
            // 处理当前工作日
            LocalDateTime dayStart = current.toLocalDate().atTime(WORK_START);
            LocalDateTime dayEnd = current.toLocalDate().atTime(WORK_END);
            
            // 计算当天的有效工作时间段
            LocalDateTime effectiveStart = current.isAfter(dayStart) ? current : dayStart;
            LocalDateTime effectiveEnd = end.isBefore(dayEnd) ? end : dayEnd;
            effectiveEnd = effectiveEnd.isAfter(dayStart) ? effectiveEnd : dayStart;
            
            // 计算当日工作时长（扣除午休）
            if (effectiveStart.isBefore(effectiveEnd)) {
                totalMinutes += calculateDailyMinutes(effectiveStart, effectiveEnd);
            }
            
            // 跳到下一天
            current = current.plusDays(1).with(WORK_START);
        }
        
        // 转换为小时并按半天取整
        double totalHours = totalMinutes / 60.0;
        return roundToHalfDay(totalHours);
    }
    
    // 计算单日内有效工作时间（分钟），扣除午休
    private static long calculateDailyMinutes(LocalDateTime start, LocalDateTime end) {
        // 工作时间段都在午休前
        if (end.isBefore(LUNCH_START.atDate(start.toLocalDate()))) {
            return ChronoUnit.MINUTES.between(start, end);
        }
        
        // 工作时间段都在午休后
        if (start.isAfter(LUNCH_END.atDate(start.toLocalDate()))) {
            return ChronoUnit.MINUTES.between(start, end);
        }
        
        // 工作时间段横跨午休
        long minutes = 0;
        
        // 午休前的工作时间
        if (start.isBefore(LUNCH_START.atDate(start.toLocalDate()))) {
            LocalDateTime preLunchEnd = end.isBefore(LUNCH_START.atDate(start.toLocalDate())) 
                    ? end : LUNCH_START.atDate(start.toLocalDate());
            minutes += ChronoUnit.MINUTES.between(start, preLunchEnd);
        }
        
        // 午休后的工作时间
        if (end.isAfter(LUNCH_END.atDate(start.toLocalDate()))) {
            LocalDateTime postLunchStart = start.isAfter(LUNCH_END.atDate(start.toLocalDate())) 
                    ? start : LUNCH_END.atDate(start.toLocalDate());
            minutes += ChronoUnit.MINUTES.between(postLunchStart, end);
        }
        
        return minutes;
    }
    
    // 判断是否为周末
    private static boolean isWeekend(LocalDateTime date) {
        DayOfWeek day = date.getDayOfWeek();
        return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY;
    }
    
    // 按半天取整（0.5天为最小单位）
    private static double roundToHalfDay(double hours) {
        double halfDayUnits = hours / HALF_DAY_HOURS;
        long fullUnits = (long) Math.floor(halfDayUnits);
        double fractional = halfDayUnits - fullUnits;
        
        // 小数部分处理规则：
        // - 小于0.25：舍去（不够0.5天）
        // - 0.25-0.75：算0.5天
        // - 大于0.75：进1天
        if (fractional >= 0.75) {
            return (fullUnits + 1) * 0.5;
        } else if (fractional >= 0.25) {
            return (fullUnits + 0.5);
        } else {
            return fullUnits * 0.5;
        }
    }

    public static void main(String[] args) {
        // 测试用例
        LocalDateTime start = LocalDateTime.of(2023, 5, 15, 10, 0); // 周一 10:00
        LocalDateTime end = LocalDateTime.of(2023, 5, 16, 16, 0);   // 周二 16:00
        
        double leaveDays = calculateLeave(start, end);
        System.out.println("请假天数: " + leaveDays); // 预期: 1.5天
    }
}